/*
 * Copyright (C) 2017-2021
 * All rights reserved, Designed By
 * Copyright authorization contact 18814114118
 */
package com.shop.cereshop.admin.service.admin.impl;

import cn.hutool.core.date.DateUtil;
import com.shop.cereshop.admin.dao.admin.AdminDAO;
import com.shop.cereshop.admin.page.index.RegionStatsVO;
import com.shop.cereshop.admin.param.admin.IndexCharts;
import com.shop.cereshop.admin.param.admin.IndexStats;
import com.shop.cereshop.admin.param.admin.StatsAmountByDay;
import com.shop.cereshop.admin.param.admin.StatsByDay;
import com.shop.cereshop.admin.param.stat.StatsParams;
import com.shop.cereshop.admin.service.admin.AdminService;
import com.shop.cereshop.commons.utils.TimeUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.YearMonth;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Map;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
public class AdminServiceImpl implements AdminService {

  @Autowired
  private AdminDAO adminDAO;

  private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");

  private static final BigDecimal ONE_HUNDRED = BigDecimal.valueOf(100).setScale(2, BigDecimal.ROUND_HALF_UP);

  @Override
  public IndexStats indexStats() {
    IndexStats stats = new IndexStats();
    String yesterday = TimeUtils.yesterday();
    String today = TimeUtils.today();
    String tomorrow = TimeUtils.getTomorrow(0);

    // 算上今天14天
    LocalDate twoWeekAgo = LocalDate.now().minusDays(13);
    Date startDate = Date.from(twoWeekAgo.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
    String startDateTime = DateUtil.format(startDate, "yyyy-MM-dd HH:mm:ss");

    // 上周时间
    String lastWeekAgoDateStr = formatter.format(twoWeekAgo.plusDays(7));

    // 查询新用户
    List<StatsByDay> twoWeekUserCount = adminDAO.selectUserCount(startDateTime, tomorrow);
    Map<String, String> userCountData = setStatsData(twoWeekUserCount, lastWeekAgoDateStr, yesterday, today);
    if (userCountData != null) {
      stats.setYesterdayNewUser(Integer.parseInt(userCountData.get("yesterdayData")));
      stats.setTodayNewUser(Integer.parseInt(userCountData.get("todayData")));
      stats.setWeekRelativeRatioNewUser(new BigDecimal(userCountData.get("relativeRate")));
    }

    // 查询访问量
    List<StatsByDay> twoWeekVisitCount = adminDAO.selectVisitCount(startDateTime, tomorrow);
    Map<String, String> visitCountData = setStatsData(twoWeekVisitCount, lastWeekAgoDateStr, yesterday, today);
    if (visitCountData != null) {
      stats.setYesterdayVisitCount(Integer.parseInt(visitCountData.get("yesterdayData")));
      stats.setTodayVisitCount(Integer.parseInt(visitCountData.get("todayData")));
      stats.setWeekRelativeRatioVisitCount(new BigDecimal(visitCountData.get("relativeRate")));
    }

    // 查询访客数
    List<StatsByDay> twoWeekVisitUser = adminDAO.selectVisitUserCount(startDateTime, tomorrow);
    Map<String, String> visitUserData = setStatsData(twoWeekVisitUser, lastWeekAgoDateStr, yesterday, today);
    if (visitUserData != null) {
      stats.setYesterdayVisitUser(Integer.parseInt(visitUserData.get("yesterdayData")));
      stats.setTodayVisitUser(Integer.parseInt(visitUserData.get("todayData")));
      stats.setWeekRelativeRatioVisitUser(new BigDecimal(visitUserData.get("relativeRate")));
    }

    // 查询店铺数
    List<StatsByDay> twoWeekShopCheck = adminDAO.selectShopCount(startDateTime, tomorrow);
    Map<String, String> shopCheckData = setStatsData(twoWeekShopCheck, lastWeekAgoDateStr, yesterday, today);
    if (shopCheckData != null) {
      stats.setYesterdayShopCount(Integer.parseInt(shopCheckData.get("yesterdayData")));
      stats.setTodayShopCount(Integer.parseInt(shopCheckData.get("todayData")));
      stats.setWeekRelativeRatioShopCount(new BigDecimal(shopCheckData.get("relativeRate")));
    }

    return stats;
  }

  /**
   * 计算统计数据
   *
   * @param twoWeekList
   * @param lastWeekAgoDateStr
   * @param yesterday
   * @param today
   * @return
   */
  public Map<String, String> setStatsData(List<StatsByDay> twoWeekList, String lastWeekAgoDateStr, String yesterday,
      String today) {
    Map<String, String> map = new HashMap<>();
    if (CollectionUtils.isNotEmpty(twoWeekList)) {
      int lastWeekSum = 0;
      int thisWeekSum = 0;
      map.put("yesterdayData", "0");
      map.put("todayData", "0");
      for (StatsByDay byDay : twoWeekList) {
        if (byDay.getStatsDate().compareTo(lastWeekAgoDateStr) < 0) {
          lastWeekSum += byDay.getTotalCount();
        } else {
          thisWeekSum += byDay.getTotalCount();
        }
        if (byDay.getStatsDate().equals(yesterday)) {
          map.put("yesterdayData", byDay.getTotalCount().toString());
        } else if (byDay.getStatsDate().equals(today)) {
          map.put("todayData", byDay.getTotalCount().toString());
        }
      }
      if (lastWeekSum != 0) {
        BigDecimal dividend = new BigDecimal((thisWeekSum - lastWeekSum) * 100);
        BigDecimal divisor = new BigDecimal(lastWeekSum);
        BigDecimal rate = dividend.divide(divisor, 2, BigDecimal.ROUND_HALF_UP);
        map.put("relativeRate", rate.toString());
      } else {
        map.put("relativeRate", ONE_HUNDRED.toString());
      }
      return map;
    }
    return null;
  }

  /**
   * 查询平台首页统计图表数据
   * <p>
   * 包括订单金额、订单量、支付人数等统计数据，以及相应的日同比和月同比数据
   * </p>
   *
   * @return IndexCharts 平台首页统计图表数据
   */
  @Override
  public IndexCharts indexCharts() {
    // 创建一个新的 IndexCharts 对象用于存储首页图表数据
    IndexCharts charts = new IndexCharts();

    // 获取今天的日期字符串 (格式: yyyy-MM-dd)
    String todayDate = TimeUtils.today();
    // 获取今天的开始时间字符串 (格式: yyyyMMdd)
    String todayTime = TimeUtils.todayTime();
    // 获取明天的开始时间字符串 (作为查询结束时间)
    String tomorrowTime = TimeUtils.getTomorrow(0);
    // 计算一周前的日期并格式化为字符串
    String oneWeekAgoDate = LocalDate.now().minusDays(6).format(formatter);
    // 构造一周前的开始时间字符串
    String oneWeekAgoTime = oneWeekAgoDate + " 00:00:00";
    // 计算一个月前的日期（实际是30天前加1天）
    LocalDate oneMonthAgo = LocalDate.now().minusMonths(1).plusDays(1);
    // 格式化一个月前的日期
    String oneMonthAgoDate = oneMonthAgo.format(formatter);
    // 构造一个月前的开始时间字符串
    String oneMonthAgoTime = oneMonthAgoDate + " 00:00:00";

    // 获取当前月份的第一天日期
    LocalDate curMonthStart = YearMonth.now().atDay(1);
    // 计算去年同月第一天日期
    LocalDate lastYearCurMonthStart = curMonthStart.minusYears(1);
    // 计算去年下个月第一天日期
    LocalDate lastYearNextMonthStart = lastYearCurMonthStart.plusMonths(1);

    // 构造当前月第一天的时间字符串
    String curMonthStartTime = curMonthStart.format(formatter) + " 00:00:00";
    // 构造下个月第一天的时间字符串
    String nextMonthStartTime = curMonthStart.plusMonths(1).format(formatter) + " 00:00:00";
    // 构造去年同月第一天的时间字符串
    String lastYearCurMonthStartTime = lastYearCurMonthStart.format(formatter) + " 00:00:00";
    // 构造去年下个月第一天的时间字符串
    String lastYearNextMonthStartTime = lastYearNextMonthStart.format(formatter) + " 00:00:00";

    // 查询从一个月前到明天的订单金额统计数据
    List<StatsAmountByDay> orderAmountList = adminDAO.selectOrderAmountList(oneMonthAgoTime, tomorrowTime);
    // 将订单金额列表转换为以日期为键的映射表，方便快速查找
    Map<String, StatsAmountByDay> orderAmountMap = orderAmountList.stream()
        .collect(Collectors.toMap(StatsAmountByDay::getStatsDate, Function.identity()));

    // 从订单金额列表中筛选出今天的订单金额，如果没有则默认为0
    BigDecimal todayOrderAmount = orderAmountList.stream()
        .filter(o -> o.getStatsDate().equals(todayDate))
        .map(StatsAmountByDay::getAmount)
        .findFirst()
        .orElse(BigDecimal.ZERO.setScale(2, BigDecimal.ROUND_HALF_UP));
    // 设置今天的订单金额
    charts.setTodayOrderAmount(todayOrderAmount);

    // 创建一个完整的日期列表，包含过去一个月每一天的数据（即使没有数据也补零）
    List<StatsAmountByDay> fullList = new ArrayList<>();
    LocalDate tmpDate = oneMonthAgo;
    // 遍历从一个月前到今天的所有日期
    while (tmpDate.isBefore(LocalDate.now().plusDays(1))) {
      String tmpDateStr = tmpDate.format(formatter);
      // 查找该日期是否有统计数据
      StatsAmountByDay day = orderAmountMap.get(tmpDateStr);
      if (day == null) {
        // 如果没有数据，则创建一个默认值的对象
        day = new StatsAmountByDay();
        day.setStatsDate(tmpDateStr);
        day.setAmount(BigDecimal.ZERO.setScale(2, BigDecimal.ROUND_HALF_UP));
      }
      fullList.add(day);
      tmpDate = tmpDate.plusDays(1);
    }
    // 设置完整的订单金额列表
    charts.setOrderAmountList(fullList);

    // 查询过去一个月的订单量数据
    List<StatsByDay> orderCountList = adminDAO.selectOrderCountList(oneMonthAgoTime, tomorrowTime);
    Map<String, StatsByDay> orderCountMap = orderCountList.stream()
        .collect(Collectors.toMap(StatsByDay::getStatsDate, Function.identity()));

    // 获取去年同期、本月等订单量统计数据
    Integer lastYearTodayOrderCount = adminDAO.selectOrderCountSum(todayTime, tomorrowTime);
    Integer curMonthOrderCount = adminDAO.selectOrderCountSum(curMonthStartTime, nextMonthStartTime);
    Integer lastYearCurMonthOrderCount = adminDAO.selectOrderCountSum(lastYearCurMonthStartTime,
        lastYearNextMonthStartTime);

    // 计算今日订单量及日同比
    int todayOrderCount = orderCountList.stream().filter(o -> o.getStatsDate().equals(todayDate))
        .mapToInt(StatsByDay::getTotalCount).findFirst().orElse(0);
    charts.setTodayOrderCount(todayOrderCount);

    List<StatsByDay> orderCountFullList = generateFullCountList(oneMonthAgo, orderCountMap);
    charts.setOrderCountList(orderCountFullList);
    if (lastYearTodayOrderCount != 0) {
      BigDecimal dividend = new BigDecimal((todayOrderCount - lastYearTodayOrderCount) * 100);
      BigDecimal divisor = new BigDecimal(lastYearTodayOrderCount);
      BigDecimal rate = dividend.divide(divisor, 2, BigDecimal.ROUND_HALF_UP);
      charts.setDayToDayOrderCountRelativeRate(rate);
    } else {
      charts.setDayToDayOrderCountRelativeRate(ONE_HUNDRED);
    }
    charts.setCurMonthOrderCount(curMonthOrderCount);

    // 计算订单量月同比
    if (lastYearCurMonthOrderCount != 0) {
      BigDecimal dividend = new BigDecimal((curMonthOrderCount - lastYearCurMonthOrderCount) * 100);
      BigDecimal divisor = new BigDecimal(lastYearCurMonthOrderCount);
      BigDecimal rate = dividend.divide(divisor, 2, BigDecimal.ROUND_HALF_UP);
      charts.setMonthToMonthOrderCountRelativeRate(rate);
    } else {
      charts.setMonthToMonthOrderCountRelativeRate(ONE_HUNDRED);
    }

    // 查询支付人数数据
    List<StatsByDay> payUserCountList = adminDAO.selectPayUserCountList(oneWeekAgoTime, tomorrowTime);
    Map<String, StatsByDay> payUserCountMap = payUserCountList.stream()
        .collect(Collectors.toMap(StatsByDay::getStatsDate, Function.identity()));

    // 获取支付人数相关统计数据
    Integer lastYearTodayPayUserCount = adminDAO.selectPayUserCountSum(todayTime, tomorrowTime);
    Integer curMonthPayUserCount = adminDAO.selectPayUserCountSum(curMonthStartTime, nextMonthStartTime);
    Integer lastYearCurMonthPayUserCount = adminDAO.selectPayUserCountSum(lastYearCurMonthStartTime,
        lastYearNextMonthStartTime);

    // 计算今日支付人数及日同比
    int todayPayUserCount = payUserCountList.stream().filter(o -> o.getStatsDate().equals(todayDate))
        .mapToInt(StatsByDay::getTotalCount).findFirst().orElse(0);
    charts.setTodayPayUserCount(todayPayUserCount);

    List<StatsByDay> payUserCountFullList = generateFullCountList(oneMonthAgo, payUserCountMap);
    charts.setPayUserCountList(payUserCountFullList);
    if (lastYearTodayPayUserCount != 0) {
      BigDecimal dividend = new BigDecimal((todayPayUserCount - lastYearTodayPayUserCount) * 100);
      BigDecimal divisor = new BigDecimal(lastYearTodayPayUserCount);
      BigDecimal rate = dividend.divide(divisor, 2, BigDecimal.ROUND_HALF_UP);
      charts.setDayToDayPayUserCountRelativeRate(rate);
    } else {
      charts.setDayToDayPayUserCountRelativeRate(ONE_HUNDRED);
    }
    charts.setCurMonthPayUserCount(curMonthPayUserCount);

    // 计算支付人数月同比
    if (lastYearCurMonthPayUserCount != 0) {
      BigDecimal dividend = new BigDecimal((curMonthPayUserCount - lastYearCurMonthPayUserCount) * 100);
      BigDecimal divisor = new BigDecimal(lastYearCurMonthPayUserCount);
      BigDecimal rate = dividend.divide(divisor, 2, BigDecimal.ROUND_HALF_UP);
      charts.setMonthToMonthPayUserCountRelativeRate(rate);
    } else {
      charts.setMonthToMonthPayUserCountRelativeRate(ONE_HUNDRED);
    }

    return charts;
  }

  /**
   * 生成有完整日期的列表
   *
   * @param oneMonthAgo
   * @param countMap
   * @return
   */
  private List<StatsByDay> generateFullCountList(LocalDate oneMonthAgo, Map<String, StatsByDay> countMap) {
    LocalDate tmpDate;
    List<StatsByDay> fullList = new ArrayList<>();
    tmpDate = oneMonthAgo;
    while (tmpDate.isBefore(LocalDate.now().plusDays(1))) {
      String tmpDateStr = tmpDate.format(formatter);
      StatsByDay day = countMap.get(tmpDateStr);
      if (day == null) {
        day = new StatsByDay();
        day.setStatsDate(tmpDateStr);
        day.setTotalCount(0);
      }
      fullList.add(day);
      tmpDate = tmpDate.plusDays(1);
    }
    return fullList;
  }

  /**
   * 根据区域参数获取用户和商家统计数据
   * 
   * @param params 统计参数，包含省、市等区域信息
   * @return RegionStatsVO 包含用户总数、新增用户数、商家总数、新增商家数及增长比的统计结果
   */
  @Override
  public RegionStatsVO getRegionUserShopStats(StatsParams params) {
    // 创建区域统计视图对象
    RegionStatsVO stats = new RegionStatsVO();

    // 设置区域信息
    if (params == null) {
      // 如果参数为空，默认查询全国数据
      stats.setProvince(null);
      stats.setCity(null);
    } else {
      stats.setProvince(params.getProvince());
      stats.setCity(params.getCity());
    }

    // 根据粒度参数设置时间范围
    LocalDate now = LocalDate.now();
    LocalDate startDateObj;
    LocalDate endDateObj;
    
    if (params != null && "day".equals(params.getGranularity())) {
        // 日粒度：查询指定日期的数据
        if (params.getDate() != null && !params.getDate().isEmpty()) {
            try {
                startDateObj = LocalDate.parse(params.getDate(), formatter);
            } catch (Exception e) {
                startDateObj = now;
            }
        } else {
            startDateObj = now;
        }
        endDateObj = startDateObj;
    } else if (params != null && "year".equals(params.getGranularity())) {
        // 年粒度：查询今年第一天到当前日期的数据
        startDateObj = now.withDayOfYear(1);
        endDateObj = now;
    } else {
        // 默认月粒度：查询当月第一天到最后一天的数据
        startDateObj = now.withDayOfMonth(1);
        endDateObj = now.withDayOfMonth(now.lengthOfMonth());
    }
    
    String startDate = startDateObj.format(formatter) + " 00:00:00";
    String endDate = endDateObj.format(formatter) + " 23:59:59";

    // 获取区域参数
    String province = params != null ? params.getProvince() : null;
    String city = params != null ? params.getCity() : null;

    // 使用区域查询获取用户总数和指定时间段内新增用户数
    Map<String, Integer> userStats = adminDAO.selectUserStatsByRegion(startDate, endDate, province, city);
    if (userStats != null) {
      // 获取用户总数
      Integer totalUserCount = userStats.get("totalUserCount");
      if (totalUserCount != null) {
        stats.setTotalUsers(totalUserCount);
      }

      // 获取指定时间段内新增用户数
      Integer yearNewUserCount = userStats.get("yearNewUserCount");
      if (yearNewUserCount != null) {
        stats.setNewUsers(yearNewUserCount);
      }

      // 计算用户增长比（新增用户/总用户数）
      if (totalUserCount != null && yearNewUserCount != null) {
        BigDecimal userGrowthRatio = calculateGrowthRatio(yearNewUserCount, totalUserCount);
        stats.setNewUsersRelativeRate(userGrowthRatio);
      }
    }

    // 获取区域商家总数和指定时间段内新增商家数
    Map<String, Integer> shopStats = adminDAO.selectShopStatsByRegion(startDate, endDate, province, city);
    if (shopStats != null) {
      // 获取商家总数
      Integer totalShopCount = shopStats.get("totalShopCount");
      if (totalShopCount != null) {
        stats.setTotalShops(totalShopCount);
      }

      // 获取指定时间段内新增商家数
      Integer yearNewShopCount = shopStats.get("yearNewShopCount");
      if (yearNewShopCount != null) {
        stats.setNewShops(yearNewShopCount);
      }

      // 计算商家增长比（新增商家/总商家数）
      if (totalShopCount != null && yearNewShopCount != null) {
        BigDecimal shopGrowthRatio = calculateGrowthRatio(yearNewShopCount, totalShopCount);
        stats.setNewShopsRelativeRate(shopGrowthRatio);
      }
    }

    return stats;
  }

  /**
   * 计算增长率
   * 
   * @param numerator   分子（新增数量）
   * @param denominator 分母（总数量）
   * @return 增长率（百分比，保留2位小数）
   */
  private BigDecimal calculateGrowthRatio(Integer numerator, Integer denominator) {
    // 检查参数有效性
    if (numerator == null || denominator == null || denominator <= 0) {
      return BigDecimal.ZERO.setScale(2, BigDecimal.ROUND_HALF_UP);
    }

    // 计算增长率：(新增数量/总数量)*100%
    return new BigDecimal(numerator)
        .divide(new BigDecimal(denominator), 2, BigDecimal.ROUND_HALF_UP)
        .multiply(BigDecimal.valueOf(100));
  }
}
